import java.io.*;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;

/**
 * 高级文件加密解密演示类
 * 包含密码保护和进度显示功能
 */
public class AdvancedFileEncryptionDemo {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("=== 高级文件加密解密系统 ===");
        System.out.println("1. 加密文件");
        System.out.println("2. 解密文件");
        System.out.println("3. 退出");
        System.out.print("请选择操作 (1-3): ");
        
        int choice = 0;
        try {
            choice = Integer.parseInt(scanner.nextLine());
        } catch (NumberFormatException e) {
            System.out.println("无效的选择，请输入1-3之间的数字。");
            return;
        }
        
        switch (choice) {
            case 1:
                encryptFileWithPassword(scanner);
                break;
            case 2:
                decryptFileWithPassword(scanner);
                break;
            case 3:
                System.out.println("程序已退出。");
                break;
            default:
                System.out.println("无效的选择，请输入1-3之间的数字。");
        }
        
        scanner.close();
    }
    
    /**
     * 使用密码加密文件
     * 
     * @param scanner 输入扫描器
     */
    private static void encryptFileWithPassword(Scanner scanner) {
        try {
            // 获取源文件路径
            System.out.print("请输入要加密的文件路径: ");
            String sourceFilePath = scanner.nextLine();
            File sourceFile = new File(sourceFilePath);
            
            if (!sourceFile.exists() || !sourceFile.isFile()) {
                System.out.println("错误: 文件不存在或不是有效的文件。");
                return;
            }
            
            // 获取目标文件路径
            System.out.print("请输入加密后的文件保存路径: ");
            String destFilePath = scanner.nextLine();
            
            // 获取密码
            System.out.print("请输入加密密码: ");
            String password = scanner.nextLine();
            
            if (password.length() < 8) {
                System.out.println("错误: 密码长度必须至少为8个字符。");
                return;
            }
            
            // 确认密码
            System.out.print("请再次输入密码确认: ");
            String confirmPassword = scanner.nextLine();
            
            if (!password.equals(confirmPassword)) {
                System.out.println("错误: 两次输入的密码不一致。");
                return;
            }
            
            // 生成密钥
            String hashedPassword = hashPassword(password);
            
            // 执行加密
            System.out.println("开始加密文件...");
            long startTime = System.currentTimeMillis();
            
            boolean result = encryptFileWithProgress(sourceFilePath, destFilePath, hashedPassword);
            
            long endTime = System.currentTimeMillis();
            
            if (result) {
                System.out.println("\n文件加密成功！");
                System.out.println("加密用时: " + (endTime - startTime) / 1000.0 + " 秒");
                System.out.println("加密文件已保存为: " + destFilePath);
            } else {
                System.out.println("\n文件加密失败。");
            }
            
        } catch (Exception e) {
            System.out.println("加密过程中发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 使用密码解密文件
     * 
     * @param scanner 输入扫描器
     */
    private static void decryptFileWithPassword(Scanner scanner) {
        try {
            // 获取源文件路径
            System.out.print("请输入要解密的文件路径: ");
            String sourceFilePath = scanner.nextLine();
            File sourceFile = new File(sourceFilePath);
            
            if (!sourceFile.exists() || !sourceFile.isFile()) {
                System.out.println("错误: 文件不存在或不是有效的文件。");
                return;
            }
            
            // 获取目标文件路径
            System.out.print("请输入解密后的文件保存路径: ");
            String destFilePath = scanner.nextLine();
            
            // 获取密码
            System.out.print("请输入解密密码: ");
            String password = scanner.nextLine();
            
            // 生成密钥
            String hashedPassword = hashPassword(password);
            
            // 执行解密
            System.out.println("开始解密文件...");
            long startTime = System.currentTimeMillis();
            
            boolean result = decryptFileWithProgress(sourceFilePath, destFilePath, hashedPassword);
            
            long endTime = System.currentTimeMillis();
            
            if (result) {
                System.out.println("\n文件解密成功！");
                System.out.println("解密用时: " + (endTime - startTime) / 1000.0 + " 秒");
                System.out.println("解密文件已保存为: " + destFilePath);
            } else {
                System.out.println("\n文件解密失败。可能是密码错误或文件已损坏。");
            }
            
        } catch (Exception e) {
            System.out.println("解密过程中发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 使用SHA-256哈希密码
     * 
     * @param password 原始密码
     * @return 哈希后的密码
     */
    private static String hashPassword(String password) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] hashedBytes = md.digest(password.getBytes());
        
        // 转换为16进制字符串
        StringBuilder sb = new StringBuilder();
        for (byte b : hashedBytes) {
            sb.append(String.format("%02x", b));
        }
        
        return sb.toString();
    }
    
    /**
     * 带进度显示的文件加密
     * 
     * @param sourceFilePath 源文件路径
     * @param destFilePath 目标文件路径
     * @param key 密钥
     * @return 是否成功
     */
    private static boolean encryptFileWithProgress(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        // 计算总大小，用于显示进度
        long totalSize = sourceFile.length();
        long processedSize = 0;
        int lastProgressPercentage = 0;
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             BufferedInputStream bis = new BufferedInputStream(fis);
             FileOutputStream fos = new FileOutputStream(destFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            
            // 生成密钥
            Key secretKey = FileEncryptionUtil.generateKey(key);
            // 初始化加密器
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            // 创建加密输出流
            CipherOutputStream cos = new CipherOutputStream(bos, cipher);
            
            byte[] buffer = new byte[8192]; // 使用更大的缓冲区提高性能
            int length;
            
            // 读取源文件并写入加密输出流
            while ((length = bis.read(buffer)) != -1) {
                cos.write(buffer, 0, length);
                
                // 更新进度
                processedSize += length;
                int progressPercentage = (int) ((processedSize * 100) / totalSize);
                
                // 只在百分比变化时更新显示
                if (progressPercentage > lastProgressPercentage) {
                    lastProgressPercentage = progressPercentage;
                    updateProgressBar(progressPercentage);
                }
            }
            
            cos.flush();
            cos.close();
            
            // 确保显示100%
            updateProgressBar(100);
            
            return true;
        } catch (Exception e) {
            System.out.println("\n加密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 带进度显示的文件解密
     * 
     * @param sourceFilePath 源文件路径
     * @param destFilePath 目标文件路径
     * @param key 密钥
     * @return 是否成功
     */
    private static boolean decryptFileWithProgress(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        // 计算总大小，用于显示进度
        long totalSize = sourceFile.length();
        long processedSize = 0;
        int lastProgressPercentage = 0;
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             BufferedInputStream bis = new BufferedInputStream(fis);
             FileOutputStream fos = new FileOutputStream(destFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            
            // 生成密钥
            Key secretKey = FileEncryptionUtil.generateKey(key);
            // 初始化解密器
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            // 创建解密输入流
            CipherInputStream cis = new CipherInputStream(bis, cipher);
            
            byte[] buffer = new byte[8192]; // 使用更大的缓冲区提高性能
            int length;
            
            // 读取加密文件并写入解密输出流
            while ((length = cis.read(buffer)) != -1) {
                bos.write(buffer, 0, length);
                
                // 更新进度（注意：解密时进度可能不太准确，因为解密后的大小可能与加密前不同）
                processedSize += length;
                int progressPercentage = (int) ((processedSize * 100) / totalSize);
                
                // 只在百分比变化时更新显示
                if (progressPercentage > lastProgressPercentage) {
                    lastProgressPercentage = progressPercentage;
                    updateProgressBar(progressPercentage);
                }
            }
            
            bos.flush();
            cis.close();
            
            // 确保显示100%
            updateProgressBar(100);
            
            return true;
        } catch (Exception e) {
            System.out.println("\n解密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 更新进度条显示
     * 
     * @param percentage 百分比
     */
    private static void updateProgressBar(int percentage) {
        final int BAR_WIDTH = 50; // 进度条宽度
        
        System.out.print("\r进度: [");
        int pos = BAR_WIDTH * percentage / 100;
        for (int i = 0; i < BAR_WIDTH; i++) {
            if (i < pos) System.out.print("=");
            else if (i == pos) System.out.print(">");
            else System.out.print(" ");
        }
        System.out.print("] " + percentage + "%");
        
        // 刷新输出
        System.out.flush();
    }
}